App.require(["math"], function (_Math) {
    function Vector2(x, y) {
        this.x = x || 0;
        this.y = y || 0;
    }
    Vector2.prototype = {
        constructor: Vector2,
        set: function (x, y) {
            this.x = x;
            this.y = y;
            return this;
        },
        clone: function () {
            return new this.constructor(this.x, this.y);
        },
        copy: function (v) {
            this.x = v.x;
            this.y = v.y;
            return this;
        },
        add: function (v) {
            this.x += v.x;
            this.y += v.y;
            return this;
        },
        addScalar: function (s) {
            this.x += s;
            this.y += s;
            return this;
        },
        addScaledVector: function (v, s) {
            this.x += v.x * s;
            this.y += v.y * s;
            return this;
        },
        sub: function (v) {
            this.x -= v.x;
            this.y -= v.y;
            return this;
        },
        subScalar: function (s) {
            this.x -= s;
            this.y -= s;
            return this;
        },
        multiply: function (v) {
            this.x *= v.x;
            this.y *= v.y;
            return this;
        },
        multiplyScalar: function (scalar) {
            this.x *= scalar;
            this.y *= scalar;
            return this;
        },
        divide: function (v) {
            this.x /= v.x;
            this.y /= v.y;
            return this;
        },
        divideScalar: function (scalar) {
            return this.multiplyScalar(1 / scalar);
        },
        applyMatrix3: function (m) {
            var x = this.x, y = this.y;
            var e = m.elements;
    
            this.x = e[0] * x + e[3] * y + e[6];
            this.y = e[1] * x + e[4] * y + e[7];
            return this;
        },
        min: function (v) {
            this.x = Math.min(this.x, v.x);
            this.y = Math.min(this.y, v.y);
            return this;
        },
        max: function (v) {
            this.x = Math.max( this.x, v.x );
            this.y = Math.max( this.y, v.y );
            return this;
        },
        limit: function (scalar) {
            var len = this.lengthSq();
            if (len > scalar * scalar) {
                return this.multiplyScalar(scalar / Math.sqrt(len));
            }
            return this;
        },
        clamp: function (min, max) {
            this.x = Math.max(min.x, Math.min(max.x, this.x));
            this.y = Math.max(min.y, Math.min(max.y, this.y));
            return this;
        },
        clampScalar: function (minVal, maxVal) {
            this.x = Math.max(minVal, Math.min(this.x, maxVal));
            this.y = Math.max(minVal, Math.min(this.y, maxVal));
            return this;
        },
        clampLength: function (min, max) {
            var length = this.length();
            return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length)));
        },
        negate: function () {
            this.x = - this.x;
            this.y = - this.y;
            return this;
        },
        dot: function (v) {
            return this.x * v.x + this.y * v.y;
        },
        lengthSq: function () {
            return this.x * this.x + this.y * this.y;
        },
        length: function () {
            return Math.sqrt(this.x * this.x + this.y * this.y);
        },
        manhattanLength: function () {
            return Math.abs(this.x) + Math.abs(this.y);
        },
        normalize: function () {
            return this.divideScalar(this.length() || 1);
        },
        angle: function () {
            var angle = Math.atan2(this.y, this.x);
            if (angle < 0) angle += 2 * Math.PI;
            return angle;
        },
        distanceTo: function (v) {
            return Math.sqrt(this.distanceToSquared(v));
        },
        distanceToSquared: function (v) {
            var dx = this.x - v.x, dy = this.y - v.y;
            return dx * dx + dy * dy;
        },
        manhattanDistanceTo: function (v) {
            return Math.abs(this.x - v.x) + Math.abs(this.y - v.y);
        },
        setLength: function (length) {
            return this.normalize().multiplyScalar(length);
        },
        equals: function (v) {
            return (v.x === this.x) && (v.y === this.y);
        },
        toArray: function (array, offset) {
            if ( array === undefined ) array = [];
            if ( offset === undefined ) offset = 0;
            array[ offset ] = this.x;
            array[ offset + 1 ] = this.y;
            return array;
        },
        rotateAround: function (center, angle) {
            var c = Math.cos(angle), s = Math.sin(angle);
    
            var x = this.x - center.x;
            var y = this.y - center.y;
    
            this.x = x * c - y * s + center.x;
            this.y = x * s + y * c + center.y;
    
            return this;
        },
        perpendicular: function () {
            var x = this.x, y = this.y;
            this.x = y;
            this.y = -x;
            return this;
        }
    };
    Vector2.add = function (a, b, c) {
        c = c || new Vector2();
        c.x = a.x + b.x;
        c.y = a.y + b.y;
        return c;
    };
    Vector2.sub = function (a, b, c) {
        c = c || new Vector2();
        c.x = a.x - b.x;
        c.y = a.y - b.y;
        return c;
    };
    Vector2.divideScalar = function (a, scalar, c) {
        c = c || new Vector2();
        c.x = a.x / scalar;
        c.y = a.y / scalar;
        return c;
    };
    Vector2.multiply = function (a, b, c) {
        c = c || new Vector2();
        c.x = a.x * b.x;
        c.y = a.y * b.y;
        return c;
    };
    Vector2.scale = function (a, b, c) {
        c = c || new Vector2();
        c.x = a.x * b;
        c.y = a.y * b;
        return c;
    }
    Vector2.divide = function (a, b, c) {
        c = c || new Vector2();
        c.x = a.x / b.x;
        c.y = a.y / b.y;
        return c;
    }
    Vector2.lerp = function (a, b, t, c) {
        var c = c || new Vector2();
        c.x = _Math.lerp(a.x, b.x, t);
        c.y = _Math.lerp(a.y, b.y, t);
        return c;
    };
    Vector2.smoothstep = function (a, b, t, c) {
        var c = c || new Vector2();
        c.x = _Math.smoothstep(a.x, b.x, t);
        c.y = _Math.smoothstep(a.y, b.y, t);
        return c;
    };
    Vector2.fromArray = function (array, offset, c) {
        c = c || new Vector2();
        offset = offset || 0;
        c.x = array[offset];
        c.y = array[offset + 1];
        return c;
    };
    Vector2.random = function () {
        return new Vector2(_Math.randFloat(-1, 1), _Math.randFloat(-1, 1));
    };

    App.define("vector2", {
        Vector2: Vector2
    })
})